ModuleClass
object. The complete module definition expression is summarized here. Each of its optional clauses is covered in greater detail later in this chapter.module ModuleNameA
[ exports variable, variable, variable, . . . ]
[ exports [ readonly ] instance variables variable, . . . ]
[ uses module, module, module, . . . ]
[ uses module with
[ imports everything ]
[ imports variable, variable, variable, . . . ]
[ imports [ readOnly ] instance variables
variable, variable, variable, . . . ]
[ excludes variable, variable, variable, . . . ]
[ excludes [ readOnly ] instance variables
variable, variable, variable, . . . ]
[ exports everything ]
[ exports variable, variable, variable, . . . ]
[ exports [ readOnly ] instance variables
variable, variable, variable, . . . ]
[ prefix prefix ]
[ renames oldName:newName, oldName:newName, . . . ]
[ renames [ readOnly ] instance variables
voldName:newName, oldName:newName, . . . ]
end]
end
module
definition expression can include two main sections, which are both optional, and can be specified in any order. The exports
options (described in the section "Exporting Variables to Other Modules" on page 202) specify which names are exported from this module. The uses
options (described in the section "Importing Variables From Other Modules" on page 205) specify which names are imported from other modules. module foo exports x, y, z uses bar with prefix Z end end
module MyModule
exports varOne, varTwo
uses foo with excludes Z end
end
module ModuleNameUnlike ScriptX global variables, which are only global within the scope of a given module, module names are truly global. There is only one namespace for module names. Because of this, you should choose a module name that is unique and distinct:
...
end
module PaintInterfaceUser
module DeveloperModule_Rev15_345a
module LotsOfSillyClassesThatOnlyPartiallyWorkTogether
By convention, module names are capitalized according to the same rules as class names. Initial letters are capitalized, with every succeeding word also capitalized.
Redefinition of a module does not break existing code at runtime. For example, if you redefine a module to exclude some variable from another module, which the module was already using, the module continues to use that variable. It is possible to import or export additional variables, but not to remove a variable from the list of variables that are imported or exported. The following example demonstrates this:
module Blue exports ink, paper end
module Pink uses Blue end
in module Blue
global ink := "blue"
global paper := "white"
in module Pink
global stone := ink + paper
"bluewhite"
-- now redefine Pink and change the value of ink from within Blue
module Pink uses Blue with excludes ink end end
in module Blue
ink := "red"
in module Pink
global Stone := ink + paper
"redwhite"
The global variable ink
is still accessible in module Pink
, even though it has been redefined to exclude ink
, and ink
continues to reflect the value that was set for it in module Blue
.
The following example shows that it is possible to end up with more than one binding for a variable in a given module:
module Green exports grass, leaves end
module Brown uses Green end
in module Green
global grass := "green"
global leaves := "green"
in module Brown
global compost := grass + leaves
"greengreen"
-- now redefine Brown so that it renames grass, and change value
module Brown uses Green with renames grass:herbs end end
in module Green
grass := "brown"
in module Brown
compost := grass + leaves
"browngreen"
global moreCompost := herbs + leaves
"browngreen"
In the module Brown
, the variable grass
, as defined by Green
, is accessible through both the original name and its new name. It has two valid bindings.
Developers should be aware that the redefinition of modules is a convenience at runtime. Redefinition of a module cannot resolve all possible cases. If a program needs to redefine a module to exclude or rename variables that are already being used in existing code, the best option is to recompile.
in module ModuleNameThe
in module
expression places the ScriptX compiler (and therefore your Listener window or authoring environment) into the scope of the module specified by ModuleName, giving you access to all the variables defined in that module or imported from other modules. If the module you specify does not exist, ScriptX reports an exception. The ScriptX compiler remains in the specified module until in module
is invoked again, placing the compiler in a different module. The in module
expression is allowed only at the top level in your program. in module
does not take a ModuleClass
object as its target. It operates on a name literal, the scripter name of a module.
Unlike other names in ScriptX, names of modules are not lexical names. With
lexical names (names of constants and variables), the name represents the
object itself. The name of a module is like a label for the module, a label which
the in module
expression recognizes. However, ScriptX functions that take a
module as a parameter, such as the generic functions load
and store
, must
be passed the ModuleClass
object itself.
If you need access to the ScriptX core classes in your module, make sure the module that you specify in an in module
expression has been defined to use the ScriptX module.
Generally, in module
is used in a ScriptX program after its module definition, but before the remainder of the expressions (class definitions, variable declarations, and so on) that make up the program to be compiled within that module.
module DefinitionModule
exports FirstClass, sumThem
-- this exports getters and setters
exports instance variables a, b, c
uses ScriptX
end
module TestingModule
uses ScriptX, DefinitionModule
end
in module DefinitionModule
class FirstClass ()
instance variables a, b, c
instance methods
method init self #rest args #key a:(10) b:(10) c:(10) -> (
apply nextMethod self args
self.a := a
self.b := b
self.c := c
)
method sumThem self -> (
print (self.a + self.b + self.c)
)
end
in module TestingModule
global t := new FirstClass a:20
sumThem t
40
A new module can be defined within the scope of any other module, with the caveat that the module cannot use another module that is not yet defined. (For information on circular relationships, see page 207.) Defining a module does not switch the compiler into that module. Only the in module
expression switches the compiler into another module..
fileIn
, implemented by DirRep
and ByteStream
, is available only with the ScriptX Language and Class Library. (The Kaleida Media Player does not include the bytecode compiler, which compiles and executes scripts.) A large ScriptX project is typically compiled from a build file, and this build file usually includes a series of calls to fileIn
. This design makes it easy to divide a large project into an number of smaller source files which are compiled in a set order.
Scripts that are imported using fileIn
run in the Scratch
module by default. The fileIn
generic function defines a module
keyword argument, which takes a ModuleClass
object. This keyword can be used to specify which module the file is compiled in.
-- the file dogs.sx will be compiled in the AnimalInterface module
fileIn name:dogs.sx module:(getModule @AnimalInterface)
The in module
expression, if it appears within a source file that is read in using fileIn
, overrides fileIn
in determining which module a given script is compiled in, but only within the scope of that file. Compilation reverts back to the previous module after fileIn
finishes compiling and executing the script and returns a value.
The fileIn
generic function It is possible to specify a module fileIn
also specifies a module using the module
keyword, the code within the file being imported is compiled within the named module. The fileIn
generic function is described in the class definition of DirRep
in the ScriptX Class Reference.
One disadvantage to using the module
keyword with fileIn
is that it can be hard to tell at a glance which module a given script is compiled in. For that reason, many programmers ignore the module
keyword. They prefer to specify the current module explicitly at the top of each source file, using in module
as the first expression in the file.
In contrast with fileIn
, scripts that are read in and compiled using the Open Title . . . menu command in the current ScriptX Listener window run in the current environment, and place the ScriptX compiler directly into the modules specified by any in module
expressions in the file.
ModuleClass
. However, unlike class or function definition expressions, the name of the module is not a variable which is assigned to that module object, so you cannot use the module name to refer to the module object as you would any other variable.
In most cases you simply use the module as an environment in which you create and manipulate other objects. There may be cases, however, in which you need access to a ModuleClass
object itself:
getModule
or currentModule
functions:getModule ModuleNameThe global function
getModule
returns the ModuleClass
object that is specified by the given name. If you do not specify a valid module, getModule
returns false
. Unlike the in module
expression, getModule
cannot define a new module. The module referred to by ModuleName must already have been defined. Note that getModule
does not affect which module ScriptX is currently compiling in. Only the in module
expression can be used to switch from one module to another.
The getModule
function returns false
if it does not return a module. You can use this feature to test whether a module is defined or not:
if not (getModule @whatever) do
(
-- This block executes only if the module @whatever is not defined
)
Once you have a module object to operate on, you can add that module to a container in the object store (as described in "Storing Modules" on page 222). For example, to append the Definition
module to the tc
title container:
append tc (getModule @Definition)
You can use any other functions that operate directly on module objects (such as fileIn
, described above). Of course, the ScriptX
, Substrate
, and Scratch
modules, which are defined by the system, cannot be saved.
currentModule()The global function
currentModule
, which is actually implemented as a macro, returns the ModuleClass
object in which ScriptX is currently compiling. It reflects the state of the compiler during compilation, and is not meant to be used at runtime. ScriptX does not have a "currently active" module at runtime.
Do not use currentModule
in scripts. It exists only for informational and debugging purposes.
deleteModule ModuleNameA module can be removed from memory using
deleteModule
, but only if it is not being used by any other module.
The global function deleteModule
can be called with either the name of the module, or a ModuleClass
object as its argument. If the module is being used by another module, and cannot be deleted, deleteModule
reports the deletingUsedModule
exception. If the modules that are using it are then deleted, the module can be deleted.
exports
section of the module definition expression:module ModuleNameThe
exports variable, variable, variable, . . .
end
exports
reserved word is followed by a list of variable names that are visible outside this module. Those variables can be variables that are defined, or will be defined, within this module. They can also be variables that were imported from other modules, and are thus "passed along" by this module. Variable names can be specified on separate lines, on the same line separated by commas, or in any combination.module Australia
uses ScriptX
exports beer, engineers
end
in module Australia
global beer := "Foster's"
global engineers := #("Wainwright", "Nicholson", "Williams")
You can use multiple exports
sections in your module definition, which is useful for documentation purposes or to group sets of exported variables together into logical groups. The resulting module exports all of the variables in all the exports
sections.
module California
uses ScriptX
exports chips, software
exports almonds, avocados, cherries, figs, lettuce, wine
exports entertainment, movies
end
in module California
global chips := #("PowerPC", "Intel")
global software := "ScriptX"
. . .
A module must explicitly export all the names that are to be visible outside the module. The point of modules is really to exclude names, to export only those names that are required by other modules. Consider the following analogies between ScriptX and C.
exports
clause is a declaration of names that are visible outside the module, analogous to the extern
statement in C.
static
. In ScriptX, a global variable is like a static variable by default. It must be explicitly exported to be visible in other modules. Thus, the default case is reversed.
exports
section of a module definition. However, be aware that exporting the class name alone does not automatically provide access to that class's methods and variables. To have full access to an exported class from another module, you must not only export the variable that contains that class, but you must also explicitly export.Person
class has two instance variables, name
and age
, and two methods, printName
and printAll
.class Person ()
instance variables
name, age
instance methods
method printName self -> (
prin ("My name is " + self.name + "\n") @Normal debug
)
method printAll self -> (
printName self
prin ("My age is " + self.age + "\n") @Normal debug
)
end
In order for both instance variables and both methods to be available outside the module, you must explicitly export all of the name bindings that are defined by the class:
module PersonModule
-- the class itself
exports Person
-- Person's instance variables (getter and setter generics)
exports nameSetter, nameGetter, ageSetter, ageGetter
-- Person's methods
exports printName, printAll
end
module ModuleNameThe
exports [ readonly ] instance variables variable, variable, . . .
end
instance variables
reserved words can be shortened to instance vars
or simply inst vars
. The list of variables can be supplied on one line separated by commas, on separate lines, or in any combination.
The optional readonly
reserved word exports the list of instance variables specified by variables
in a read-only form; that is, they can be queried but not changed.
The instance variables
part of an exports
clause is simply shorthand for exporting the setter and getter generic functions for those variables. If the readonly
option is specified, only getter methods are exported. The following module definitions are equivalent:
module MyModule
exports readonly instance variables x
exports instance variables y
end
module MyModule
exports xGetter, ySetter, yGetter
end
uses
clause of a module definition. There are two forms of uses
: the short form (uses
) that simply imports all the exported variables from the given module into this module, and the long form (uses
. . . with
), which allows control over which variables should be imported, and how they should be handled. For example, a uses
. . . with
clause can change their names or reexport them.
For the complete syntax for defining a module, see page 196. The syntax for both uses
clauses in a module definition is as follows:
module ModuleNameThe line containing the reserved word
[ uses module, module, module, . . . ]
[ uses module with
[ imports everything ]
[ imports variable, variable, variable, . . . ]
[ imports [ readOnly ] instance variables
variable, variable, variable, . . . ]
[ excludes variable, variable, variable, . . . ]
[ excludes [ readOnly ] instance variables
variable, variable, variable, . . . ]
[ exports everything ]
[ exports variable, variable, variable, . . . ]
[ exports [ readOnly ] instance variables
variable, variable, variable, . . . ]
[ prefix prefix ]
[ renames oldName:newName, oldName:newName, . . . ]
[ renames [ readOnly ] instance variables
voldName:newName, oldName:newName, . . . ]
end]
end
uses
specifies the modules whose variables are to be imported. The first form is simply the reserved word uses
followed by the other modules whose variables are to be imported. Note that with this form, all variables are imported from the specified modules. The modules can be specified on the same line separated by commas, on separate lines, or in any combination. Any module you specify in a uses
definition must already have been defined. You can have a single uses
, or you can have multiple uses
clauses in the same module definition. Note that if you want the variables in the ScriptX core classes to be available to the expressions in this module, you have to explicitly use the ScriptX module.
The second form of uses
clause, uses with
, allows more control over how imported variables from individual modules are handled. The uses with
clause specifies options for a single module. You must use an individual uses with
clause for each module.
The uses with
clause contains several sub-clauses, referred to in this chapter as options, all of which are optional and may be included in any order. Also, although they have been presented here on multiple lines and indented, they may also be specified on a single line, or in any combination.
The uses
form is equivalent to the uses with
form with imports everything
. That is, the following two definitions are equivalent:
module Mocha
uses ScriptX
end
module Mocha
uses ScriptX with
imports everything
end
All uses with
option are described in the following sections.
module ModuleNameThis is the simplest form of module definition that imports variables from other modules. All variables that are exported from the specified modules are imported into the module given by ModuleName.
uses module, module, module, . . .
end
uses
clauses that import variables from each other. In this example, Foo
uses Bar
which uses Foo
:-- Don't define a circular relationship like this
module Foo
exports x, y, z
uses Bar
end
module Bar
exports a, b, c
uses Foo
end
Because a module's uses
clause can only name modules that have already been defined, explicit circular use relationships cannot occur (the first definition returns a warning stating that module bar
does not exist).
It is possible to define implicit circular use relationships between modules by exporting variable names from one module and defining those variables in another. This method of defining and using modules is described in "Organizing Modules" on page 214.
module ModuleNameThe optional
uses module with
imports everything
imports variable, variable, variable, . . .
imports [ readOnly ] instance variables
variable, variable, variable, . . . ]
end
end
imports
clause is used to specify which variables to import from the module specified by module.
The imports
option, when used with the reserved word everything
, simply imports all the variables that the given module exports. If you use imports everything
, you cannot use any of the other forms of imports
in the same uses with
clause. Also, if you omit all imports
options in a uses with
clause, imports everything
is assumed. The advantage to using imports everything
rather than a simple uses
clause is that other options, such as prefix
and renames
, are also available.
The imports
option followed by a list of variables specifies exactly which variables to import into this module. The variable names can be specified on separate lines, on the same line separated by commas, or in any combination.
The imports
option with the instance variables
reserved words (or the readOnly
instance variables
reserved words) is syntactic shorthand for importing the setter and getter generic functions for the named instance variables (or, in the case of readOnly
, only the getter method). The instance variables
reserved words can be shortened to instance vars
or simply inst vars
. See "Exporting Classes" on page 203 for more information on importing and exporting classes and their methods and variables.
The following example creates a module and exports three global variables it defines.
module XYZ
uses ScriptX
exports x, y, z
end
in module XYZ
global x:10, y:"foo", z:#(56,567)
The following module uses XYZ
and imports all variables from it.
module XYZimport1
uses ScriptX
uses XYZ with imports everything end
end
in module XYZimport1
print x
10
print y
"foo"
print z
#(56, 567)
The third module uses XYZ
, but it imports only x
and z
from this module. Since y
is undefined within this module, attempting to access y
from this module reports an exception.
module XYZimport2
uses ScriptX
uses XYZ with imports x, z end
end
in module XYZimport2
print x
10
print z
#(56,567)
print y -- this reports an exception
-- ** XYZimport2:y does not have a variable value \
(UninitializedVariable)
module ModuleNameThe optional
uses module with
renames oldName:newName, oldName:newName, . . .
renames [ readOnly ] instance variables
oldName:newName, oldName:newName, . . .
end
end
renames
clause is used to import a variable from the given module and give it a new name. The definition of the variable, if any, is still valid in the new module under the new name. The renames
keyword is followed by any number of oldName and newName pairs, on the same line separated by commas, on separate lines, or in any combination. The old and new variable names, oldName and newName, are separated by colons. The renames
clause, used without an imports
clause, assumes imports everything
.
The renames
clause with the instance variables
reserved words (or the readOnly instance variables
reserved words) is syntactic shorthand for renaming the setter and getter generic functions for the named instance variables (or, in the case of readOnly
, only the getter method). See "Exporting Classes" on page 203 for more information on importing and exporting classes and their methods and variables.
The following script sets up module XYZ
, as in the previous example.
module XYZ
uses ScriptX
exports x, y, z
end
in module XYZ
global x:10, y:"foo", z:#(56,567)
This module uses module XYZ
, imports only x
, and renames it externalX
.
module XYZrename1
uses ScriptX
uses XYZ with
renames x:externalX
end
end
in module XYZrename1
print externalX
10
print y
"foo"
The next module explicitly imports x
and y
, but renames x
. Renaming x
overrides import x
so that x
is imported, but with a new name.
module XYZrename2
uses ScriptX
uses XYZ with
imports x, y
renames x:otherX
end
end
in module XYZrename2
-- this reports an exception
print x
-- ** XYZrename2:x does not have a variable value \
(UninitializedVariable)
print otherX
10
print y
"foo"
module ModuleNameThe optional
uses module with
prefix prefix
end
end
prefix
clause is used to import variables and rename them by attaching a prefix (specified by prefix) to the variable name. Prefixing variable names is useful for resolving conflicts in variable names between modules or for simply indicating which module a variable came from. The prefix
option, used without an imports
option, assumes imports everything
.
You can assign a prefix to all imported variables and then rename specific variables by using the renames
clause for those variables.
The following script sets up module XYZ
, as in the previous example.
module XYZ
uses ScriptX
exports x, y, z
end
in module XYZ
global x:10, y:"foo", z:#(56,567)
Module XYZprefix1
imports all variables from module XYZ
and prefixes variables from that module with XYZ_
.
module XYZprefix1
uses ScriptX
uses XYZ with
prefix XYZ_
end
end
in module XYZprefix1
print x
-- ** XYZprefix1:x does not have a variable value
(UninitializedVariable)
print XYZ_x
10
print XYZ_y
"foo"
Module XYZprefix2
uses both a prefix
clause and a renames
clause. The renames
clause overrides the prefix for specific variables.
module XYZprefix2
uses ScriptX
uses XYZ with
prefix ext_
renames x:fumbleWhizzy
end
end
in module XYZprefix2
print ext_x
-- ** XYZprefix2:ext_x does not have a variable value
(UninitializedVariable)
print ext_y
"foo"
print fumbleWhizzy
10
module ModuleNameThe optional
uses module with
excludes variable, variable, variable, . . .
excludes [ readOnly ] instance variables
variable, variable, variable, . . .
end
end
excludes
clause is used to explicitly exclude individual variables from a module. The list of variable names can be on a single line separated by commas, on individual lines, or in any combination. If you use excludes
without an imports
clause, imports everything
is assumed.
The excludes
option with the instance variables
reserved words (or the readOnly instance variables
reserved words) is syntactic shorthand for excluding the setter and getter generic functions for the named instance variables (or, in the case of readOnly
, only the getter method). See "Exporting Classes" on page 203 for more information on importing and exporting classes and their methods and variables.
The example uses module XYZ
, just as in the previous section.
module XYZ
uses ScriptX
exports x, y, z
end
in module XYZ
global x:10, y:"foo", z:#(56,567)
This module imports all the variables from module XYZ
, excluding y
.
module XYZexclude1
uses ScriptX
uses XYZ with
imports everything
excludes y
end
end
in module XYZexclude1
print x
10
print y
-- ** XYZexclude1:y does not have a variable value \
(UninitializedVariable)
module ModuleNameThe optional
uses module with
exports everything
exports variable, variable, variable, . . .
exports [ readOnly ] instance variables
variable, variable, variable, . . .
end
end
exports
clause is used to re-export any variables that have been imported from module using the imports
or renames
clauses (sometimes called "transitive exporting"). You also use it to export variables created within this module. If the variables have been renamed or prefixed upon import (using the renames
or prefix
clauses), those variables are exported using those new names.
The exports
option, when used with the reserved word everything
, simply re-exports all the variables that were imported from module. If you use exports everything
, you cannot use any of the other forms of exports
in the same uses with
clause.
The exports
option followed by a list of variables specifies exactly which of the variables to re-export. The variable names can be specified on separate lines, on the same line separated by commas, or in any combination.
The exports
option with the instance variables
reserved words (or the readOnly
instance variables
reserved words) is syntactic shorthand for re-exporting the setter and getter generic functions for the named instance variables (or, in the case of readOnly
, only the getter method). See "Exporting Classes" on page 203 for more information on importing and exporting classes and their methods and variables. The instance variables
reserved words can be shortened to instance vars
or simply inst vars
. All are equivalent.
The example uses module XYZ
, just as in the previous section.
module XYZ
uses ScriptX
exports x, y, z
end
in module XYZ
global x:10, y:"foo", z:#(56,567)
Module XYZexport
, which exports its own variables (a
and b
), imports everything from module XYZ
, renames x
to otherX
, and transitively exports otherX
and y
.
module XYZexport
exports a, b
uses ScriptX
uses XYZ with
imports everything
renames x:otherX
exports otherX, y
end
end
in module XYZexport
global a := "croissant"
global b := pi
print otherX
10
Module moreXYZexport
uses XYZexport
and imports all. The variables that get imported are y
(from module XYZ
), otherX
(which is actually the variable x
from module XYZ
) and a
and b
(from module XYZexport
).
module moreXYZexport
uses ScriptX
uses XYZexport with
imports everything
end
end
in module moreXYZexport
print a
"croissant"
print b
3.14159
print y
"foo"
print otherX
10
print x
-- ** moreXYZexport:x does not have a variable value \
(UninitializedVariable)
This document is part of the ScriptX Language Guide, one of the volumes of the ScriptX Technical Reference Series. ScriptX is developed by the ScriptX Engineering Team at Apple Computer, successor to the Kaleida Engineering Team at Kaleida Labs, Inc.